Crate derive_visitor

source ·
Expand description

This crate derives visitor pattern for arbitrary data structures. This pattern is particularly useful when dealing with complex nested data structures, abstract trees and hierarchies of all kinds.

The main building blocks of this crate are two derivable traits:

  • Visitor and VisitorMut implementations walk through data structures and accumulate some information;
  • Drive and DriveMut implementations are data structures that know how to drive a visitor through themselves.

Please refer to these traits’ documentation for more details.

§Example

§Immutable visitor that counts items in a tree

use derive_visitor::{Visitor, Drive};

#[derive(Drive)]
struct Directory {
    #[drive(skip)]
    name: String,
    items: Vec<DirectoryItem>,
}

#[derive(Drive)]
enum DirectoryItem {
    File(File),
    Directory(Directory),
}

#[derive(Drive)]
struct File {
    #[drive(skip)]
    name: String,
}

#[derive(Visitor, Default)]
#[visitor(File(enter), Directory(enter))]
struct Counter {
    files: u32,
    directories: u32
}

impl Counter {
    fn enter_file(&mut self, _file: &File) {
        self.files += 1;
    }
    fn enter_directory(&mut self, _directory: &Directory) {
        self.directories += 1;
    }
}

let mut counter = Counter::default();

let example_directory = Directory {
    name: "root".into(),
    items: vec![
        DirectoryItem::Directory(
            Directory {
                name: "home".into(),
                items: vec![
                    DirectoryItem::File(File { name: "README.md".into() }),
                    DirectoryItem::File(File { name: "Star Wars.mov".into() })
                ]
            }
        ),
        DirectoryItem::Directory(
            Directory { name: "downloads".into(), items: vec![] }
        )
    ],
};

example_directory.drive(&mut counter);

assert_eq!(counter.files, 2);
assert_eq!(counter.directories, 3);

§Mutable visitor that alters a tree

use derive_visitor::{VisitorMut, DriveMut};

#[derive(DriveMut)]
struct Tree {
    #[drive(skip)]
    name: String,
    children: Vec<Tree>
}

#[derive(VisitorMut, Default)]
#[visitor(Tree(enter))]
struct Renamer {
    from: &'static str,
    to: &'static str,
}

impl Renamer {
    fn enter_tree(&mut self, tree: &mut Tree) {
        tree.name = tree.name.replace(self.from, self.to);
    }
}

let mut my_tree = Tree{
    name: "old parent".to_string(),
    children: vec![
        Tree {
            name: "old child".to_string(),
            children: vec![]
        }
    ]
};

my_tree.drive_mut(&mut Renamer{from: "old", to: "new"});

assert_eq!(my_tree.name, "new parent");
assert_eq!(my_tree.children[0].name, "new child");

§Features

  • std-types-drive - implement Drive for primitive types and String type from std. It is recommended to either skip these types in your Drive implementation, or to wrap them with newtypes, so this feature is disabled by default. However it might be useful when driving through autogenerated structs.

Structs§

Enums§

  • Defines whether an item is being entered or exited by a visitor.

Traits§

  • A data structure that can drive a visitor through itself.
  • Drive a VisitorMut over this datastructure.
  • An interface for visiting arbitrary data structures.
  • An interface for visiting data structures and mutating them during the visit.

Functions§

Derive Macros§